﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Threading;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.IO.IsolatedStorage;
using System.Windows.Resources;
using System.Windows.Media.Imaging;
using System.Diagnostics;

namespace Delay
{
    public static class NetWork
    {
        public static bool isNonPictureMode = false;
        private const int WorkItemQuantum = 5;
        private static readonly Thread _thread = new Thread(WorkerThreadProc);
        private static readonly Queue<PendingRequest> _pendingRequests = new Queue<PendingRequest>();
        private static readonly Queue<PendingCompletion> _pendingCompletion = new Queue<PendingCompletion>();
        private static readonly object _syncBlock = new object();
        private static bool _exiting;
        /// <summary>
        /// Gets the value of the Uri to use for providing the contents of the Image's Source property.
        /// </summary>
        /// <param name="obj">Image needing its Source property set.</param>
        /// <returns>Uri to use for providing the contents of the Source property.</returns>
        [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "UriSource is applicable only to Image elements.")]
        public static Uri GetUriSource(DependencyObject obj)
        {
            if (null == obj)
            {
                throw new ArgumentNullException("obj");
            }
            return (Uri)obj.GetValue(UriSourceProperty);
        }
        /// <summary>
        /// Sets the value of the Uri to use for providing the contents of the Image's Source property.
        /// </summary>
        /// <param name="obj">Image needing its Source property set.</param>
        /// <param name="value">Uri to use for providing the contents of the Source property.</param>
        [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "UriSource is applicable only to Image elements.")]
        public static void SetUriSource(DependencyObject obj, Uri value)
        {
            if (null == obj)
            {
                throw new ArgumentNullException("obj");
            }
            obj.SetValue(UriSourceProperty, value);
        }
        /// <summary>
        /// Identifies the UriSource attached DependencyProperty.
        /// </summary>
        public static readonly DependencyProperty UriSourceProperty = DependencyProperty.RegisterAttached(
            "UriSource", typeof(Uri), typeof(NetWork), new PropertyMetadata(OnUriSourceChanged));
        /// <summary>
        /// Gets or sets a value indicating whether low-profile image loading is enabled.
        /// </summary>
        public static bool IsEnabled { get; set; }
        [SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline", Justification = "Static constructor performs additional tasks.")]
        static NetWork()
        {
            // Start worker thread
            _thread.Start();
            Application.Current.Exit += new EventHandler(HandleApplicationExit);
            IsEnabled = true;
        }
        private static void HandleApplicationExit(object sender, EventArgs e)
        {
            // Tell worker thread to exit
            _exiting = true;
            if (Monitor.TryEnter(_syncBlock, 100))
            {
                Monitor.Pulse(_syncBlock);
                Monitor.Exit(_syncBlock);
            }
        }
        [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Relevant exceptions don't have a common base class.")]
        private static void WorkerThreadProc(object unused)
        {
            Queue<PendingRequest> pendingRequests = new Queue<PendingRequest>();
            Queue<PendingCompletion> pendingCompletions = new Queue<PendingCompletion>();
            while (!_exiting)
            {
                lock (_syncBlock)
                {
                    // Wait for more work if there's nothing left to do
                    if ((0 == _pendingRequests.Count) && (0 == pendingRequests.Count) && (0==_pendingCompletion.Count))
                    {
                        Monitor.Wait(_syncBlock);
                        if (_exiting)
                        {
                            return;
                        }
                    }
                    // Copy work items to private collections
                    while (0 < _pendingRequests.Count)
                    {
                        pendingRequests.Enqueue(_pendingRequests.Dequeue());
                    }
                    while (0 < _pendingCompletion.Count)
                    {
                        pendingCompletions.Enqueue(_pendingCompletion.Dequeue());
                    }
                }
               
                // Process pending requests
                for (var i = 0; (i < pendingRequests.Count) && (i < WorkItemQuantum); i++)
                {
                    var pendingRequest = pendingRequests.Dequeue();

                    //AndreasHammar 2011-01-25: is getting this in the Tre.News app
                    if (pendingRequest.Uri == null) continue;
                    if (pendingRequest.ResolvingObject is DowloadManagerItem)//图片的时候 缓存就不再继续下载
                    {
                        if (pendingRequest.Uri.IsAbsoluteUri)
                        {//绝对路径
                           
                            //load from isolated storage if has been cached
                            DowloadManagerItem dmi = pendingRequest.ResolvingObject as DowloadManagerItem;
                            if (dmi.Type == "complate" ||dmi.Type == "ready")
                            {
                                    pendingCompletions.Enqueue(new PendingCompletion(pendingRequest.ResolvingObject, pendingRequest.Uri));
                            }
                            else 
                            {
                                var webRequest = HttpWebRequest.CreateHttp(pendingRequest.Uri);
                                webRequest.AllowReadStreamBuffering = false; // Don't want to block this thread or the UI thread on network access
                                if (dmi.HasFilesize > 0)
                                {
                                    string Range = "bytes=" + dmi.HasFilesize.ToString() + "-";
                                    webRequest.Headers[HttpRequestHeader.Range] = Range;
                                }
                                webRequest.BeginGetResponse(HandleGetResponseResult, new ResponseState(webRequest, pendingRequest.ResolvingObject, pendingRequest.Uri));

                            }
                        }
                        else
                        {
                            
                        }
                    }
                    Thread.Sleep(1);
                }
                if (0 < pendingCompletions.Count)
                {
                    Deployment.Current.Dispatcher.BeginInvoke(() =>
                    {
                        while (0 < pendingCompletions.Count)
                        {
                            var pendingCompletion = pendingCompletions.Dequeue();
                        }
                    });
                }
                Thread.Sleep(1);
            }
        }
        private static string GetParsePath(string url)
        {
            return url.Replace("://", "").Replace("/","_");
        }
        private static bool CacheImage(byte[] BufferRead, Uri u, int len)
        {
            string path = u.AbsolutePath;
            string filename = path.Substring(path.LastIndexOf("/")+1);
            string filePath = System.IO.Path.Combine("Xmls", filename);
            using (var store = IsolatedStorageFile.GetUserStoreForApplication())
            {
                try
                {
                    if (!store.DirectoryExists("Xmls"))
                    {
                        store.CreateDirectory("Xmls");
                    }
                    using (var stream = store.OpenFile(filePath, FileMode.OpenOrCreate, FileAccess.Write))
                    {
                        if (stream.Length > 0)
                        {
                            stream.Seek(0, SeekOrigin.End);
                        }


                        stream.Write(BufferRead, 0, len);
                    }
                    return true;
                }
                catch (Exception e)
                {
                    Debug.WriteLine(e.Message);
                    return false;
                    throw;
                }
            }
        }
        private static void OnUriSourceChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
        {
            var resolving = (DependencyObject)o;
            var uri = (Uri)e.NewValue;

                lock (_syncBlock)
                {
                    // Enqueue the request
                    _pendingRequests.Enqueue(new PendingRequest(resolving, uri));
                    Monitor.Pulse(_syncBlock);
                }
            
        }
        private static void HandleGetResponseResult(IAsyncResult result)
        {
            ResponseState response = (ResponseState)result.AsyncState;
            response.httpRespone = (HttpWebResponse)response.WebRequest.EndGetResponse(result);
            string filesize = response.httpRespone.Headers[HttpRequestHeader.ContentLength];
            if (((DowloadManagerItem)response.ResolvingObject).TotalSize==0)
            {
                Deployment.Current.Dispatcher.BeginInvoke(() =>
                {
                    ((DowloadManagerItem)response.ResolvingObject).TotalSize = int.Parse(filesize);

                });
            }
            int total = 0;  
            using (Stream responseStream = response.httpRespone.GetResponseStream())
            {
                int osize = responseStream.Read(response.BufferRead, 0, ResponseState.BUFFER_SIZE);
                DowloadManagerItem dmi = response.ResolvingObject as DowloadManagerItem;               
                while (osize > 0)
                {
                    
                    if (dmi.Type == "pause")
                    {
                        break;
                    }
                    total += osize;
                    if (CacheImage(response.BufferRead, response.Uri, osize) == false)
                    {
                        return;                           
                    }
                    Deployment.Current.Dispatcher.BeginInvoke(() =>
                    {
                       
                        dmi.HasFilesize = total;
                        if (dmi.Type != "prog")
                        {
                          dmi.Type = "prog";
                        }
                    });
                   osize = responseStream.Read(response.BufferRead, 0, ResponseState.BUFFER_SIZE);
                }
                if (dmi.Type == "prog")
                {
                    System.Windows.Deployment.Current.Dispatcher.BeginInvoke(() =>
                    {
                        Debug.WriteLine("dmi.HasFilesize");
                        dmi.HasFilesize = total;
                        dmi.Type = "complate";
                        dmi.Result = 1;
                    });
                }
                else if (dmi.Type == "pause")
                {
                    dmi.HasFilesize = total;
                    dmi.Result = 1;
                }
                lock (_syncBlock)
                {
                    _pendingCompletion.Enqueue(new PendingCompletion(response.ResolvingObject, response.Uri));
                    Monitor.Pulse(_syncBlock);
                }

            }
           

        }
        private class PendingRequest
        {
            public DependencyObject ResolvingObject { get; private set; }
            public Uri Uri { get; private set; }
            public PendingRequest(DependencyObject resolving, Uri uri)
            {
                ResolvingObject = resolving;
                Uri = uri;
            }
        }
        private class ResponseState
        {
            public const int BUFFER_SIZE = 1024 * 32;
            public byte[] BufferRead;
            public HttpWebResponse httpRespone;
            public Stream httpStream { get; set; }
         //   public Stream responseStream;//使用完成后务必关闭
            public WebRequest WebRequest { get; private set; }
            public DependencyObject ResolvingObject { get; private set; }
            public Uri Uri { get; private set; }
            public ResponseState(WebRequest webRequest, DependencyObject resolving, Uri uri)
            {
                WebRequest = webRequest;
                ResolvingObject = resolving;
                Uri = uri;
                BufferRead = new byte[BUFFER_SIZE];
            }
        }
        private class PendingCompletion
        {
            public DependencyObject ResolvingObject { get; private set; }
            public Uri Uri { get; private set; }
            public PendingCompletion(DependencyObject resolving, Uri uri)
            {
                ResolvingObject=resolving;
                Uri = uri;
            }
        }
    }
}
